<template>
  <div class="remote-select-wrapper" ref="remoteSelectWrapperRef">
    <a-select
      v-model="selectedValue"
      :allow-clear="allowClear"
      :loading="fetching"
      :filter-option="false"
      :trigger-props="{ updateAtScroll, scrollToClose }"
      placeholder="请选择"
      allow-search
      v-bind="$attrs"
      :popup-container="mergedPopupContainer"
      @popup-visible-change="visibleChange"
      @search="fetchListApi"
      @change="handleChangeFn"
      @blur="handleBlurFn"
      @focus="handleFocusFn"
    >
      <template #label="{ data }">
        <slot name="label" :data="data">
          {{ selector.labelFn ? selector.labelFn(selectedOption) : data.label }}
        </slot>
      </template>
      <template #empty>
        <!-- 请选择上级 -->
        <a-alert v-if="mustParentShow" type="warning">
          <template #icon>
            <icon-exclamation-circle />
          </template>
          {{ mustParentTip }}
        </a-alert>
      </template>
      <a-option
        v-for="(item, optionIndex) in options"
        :key="item[selector.key]"
        :value="item[selector.key].toString()"
        :label="item[selector.label]"
        :disabled="item.disabled"
      >
        <slot name="option" :option="item" :index="optionIndex">
          <template v-if="selector.tag">
            <!-- 员工状态 -->
            <!-- 在职：1（blue）停用：2（gray） 离职：0（orange） -->
            <a-tag
              class="staff-status"
              size="small"
              v-if="selector.tag == 'staff'"
              :color="item.status == 1 ? 'blue' : item.status == 2 ? 'gray' : 'orange'"
              >{{ item.statusName }}</a-tag
            >
          </template>
          {{ selector.optionFn ? selector.optionFn(item) : item[selector.label] }}
        </slot>
      </a-option>
    </a-select>
  </div>
</template>

<script setup name="RemoteEachSelect">
  import { ref, computed, watch, readonly, onMounted } from 'vue'

  const props = defineProps({
    modelValue: [Number, String],
    type: {
      type: String,
      require: true,
    },
    selector: Object,
    initStr: String,
    // 接口参数
    params: {
      type: Object,
      default: () => {},
    },
    allowClear: {
      type: Boolean,
      default: () => true,
    },
    index: [Number, String],
    // 滚动容易导致下拉菜单分离
    updateAtScroll: {
      type: Boolean,
      default: () => false,
    },
    // 滚动关闭下拉菜单
    scrollToClose: {
      type: Boolean,
      default: () => false,
    },
    // 级联相关
    mustParentShow: Boolean,
    mustParentTip: {
      type: String,
      default: () => '请先选择上级',
    },
    hookChange: Object,
    popupContainer: [String, Object],
    disabledValue: [String, Number],
    // 默认是否加载数据，优先lazyloading
    mountedLoad: {
      type: Boolean,
      default: true,
    },
    // 延迟加载，默认不加载blur时候加载
    lazyloading: {
      type: Boolean,
      default: () => false,
    },
  })
  const emit = defineEmits(['focus', 'blur', 'update:modelValue', 'change', 'initList', 'listGet'])

  const mergedPopupContainer = ref(props.popupContainer || 'body')
  const remoteSelectWrapperRef = ref()
  if (!props.popupContainer) {
    onMounted(() => {
      mergedPopupContainer.value = remoteSelectWrapperRef.value
    })
  }
  const selector = readonly(props.selector)
  const options = ref([])
  const keyword = ref('')
  const lastFetchId = ref(0)
  const fetching = ref(false)

  // 当前选中的选项
  const selectedOption = computed(() => {
    if (options.value && options.value.length) {
      const current = options.value.find((item) => item[selector.key || 'id'] == props.modelValue)
      return current || {}
    } else {
      return {}
    }
  })

  const selectedValue = computed({
    get() {
      if (!props.modelValue) return undefined
      // 当前value在列表中，则显示为value，否则为显示undefined
      const current = options.value.find((item) => item[selector.key || 'id'] == props.modelValue)
      const selectedValue = current
        ? props.modelValue.toString() // 防止值是字符串和数字类型不匹配
        : props.initStr
      return selectedValue
    },
    set(val) {
      emit('update:modelValue', val)
    },
  })
  // change 事件
  const handleChangeFn = (value) => {
    const option = options.value.find((item) => item[selector.key] == value) || {}
    emit('change', value, option, props.index)
    props.hookChange.change()
  }
  // 初始化 emit 结果数据
  let initListEmit = false
  // 接口调用
  const fetchListApi = async (value) => {
    if (props.mustParentShow == true) {
      options.value = []
      return
    }

    fetching.value = true
    lastFetchId.value += 1
    const fetchId = lastFetchId.value
    const queryParam = {
      ...selector.data,
      ...props.params,
    }
    // 增加关键字参数
    keyword.value = value
    queryParam[selector.keyword] = value

    const res = await selector.api(queryParam)
    fetching.value = false
    if (res.code == 200) {
      if (fetchId !== lastFetchId.value) {
        // 非顺序
        return
      }
      const result = res.result || [] // 可能为null
      // 兼容
      if (result.data) {
        options.value = result.data
      } else {
        options.value = result
      }
      // 过滤禁用
      options.value = options.value.filter((it) => it[selector.key] != props.disabledValue)

      // 只 emit 一次
      if (!initListEmit) {
        emit('initList', options.value)
        initListEmit = true
      }

      // 每次都会 emit 的事件
      emit('listGet', options.value)
    }
  }

  const staticParams = computed(() => {
    return JSON.stringify(props.params)
  })
  watch(
    () => staticParams.value,
    () => {
      fetchListApi()
    }
  )
  onMounted(() => {
    if (props.mountedLoad == false) return
    if (props.lazyloading == true) return
    fetchListApi()
  })

  // 打开时 拿无关键字列表
  const visibleChange = (visible) => {
    if (visible == true) {
      if (keyword.value || props.lazyloading == true) {
        fetchListApi()
      }
    }
  }

  // 获取焦点
  const handleFocusFn = () => {
    emit('focus')
  }
  // 失去焦点
  const handleBlurFn = () => {
    emit('blur')
  }

  watch(
    () => props.mountedLoad,
    (val) => {
      if (val == true) {
        fetchListApi()
      } else {
        options.value = []
      }
    }
  )
</script>

<style lang="less" scoped>
  .remote-select-wrapper {
    width: 100%;
    position: relative;
  }
  .staff-status {
    padding: 0 3px;
  }
</style>
